#ifndef JMETATYPE_H
#define JMETATYPE_H

#include <JXQCore/JSingleton>
#include <JXQCore/JDebug>
#include <unordered_map>
#include <functional>
#include <typeinfo>
#include <memory>
#include <atomic>

namespace JXQ
{

class JMeta;
class JTypeIDConstructHelper;
class TypeInfoPrivate;

// 类型信息
class JXQLIB_EXPORT JTypeInfo
{
public:
	std::string name() const; //类型名
	size_t size() const;      //类型大小
	bool isPrimitive() const; //是否是基本类型
	bool isFloat() const;     //是否时浮点数
	bool isPointer() const;   //是否是指针

public: // 类型映射字符串
	template <typename T>
	inline static constexpr const char *getName(T);

	template <typename T>
	inline static constexpr const char *getName();

public:
	// 是否是基本类型（包括指针）
	template <typename T> inline static constexpr bool isPrimitive();
	template <typename T> inline static constexpr bool isPrimitive(T);

	// 是否是浮点数(float / double / long double)
	template <typename T> inline static constexpr bool isFloat();
	template <typename T> inline static constexpr bool isFloat(T);

	// 是否是指针
	template <typename T> inline static constexpr bool isPointer();
	template <typename T> inline static constexpr bool isPointer(T);

public:
	static const JTypeInfo voidType;
	bool operator==(const JTypeInfo &info) const;
	bool operator!=(const JTypeInfo &info) const;
	bool operator==(const std::type_info &info) const;
	bool operator!=(const std::type_info &info) const;

	const std::type_info& stdTypeInfo() const;

protected:
	JTypeInfo(const std::type_info &info, size_t size,
			  bool isPrimitive, bool isFloat, bool isPointer);

private:
	~JTypeInfo();
	TypeInfoPrivate *d_ptr;

	typedef std::unordered_map<std::string, size_t>  TypeIdHash;
	static TypeIdHash typeIdHash;

	typedef std::unordered_map<size_t, const JTypeInfo*>  TypeInfoHash;
	static TypeInfoHash typeInfoHash;

	friend class JMeta;
	friend class JTypeIDConstructHelper;
	J_DISABLE_COPY_MOVE(JTypeInfo)
};

class JTypeIDConstructHelper
{
public:
	template <typename T>
	inline static const JXQ::JTypeInfo &jtypeid();
};

/*-----------------------------------------------------------------------------------*/

// 通用有参数构造方法类
template <typename ...Args>
struct JConstructionMethod
{
	typedef std::function<void*(Args&&...)>  ConstructMethod;
	ConstructMethod constructMethod;
	JConstructionMethod(ConstructMethod &&constructMethod) :
		constructMethod(std::forward<ConstructMethod>(constructMethod)) {}
};

// 无参数构造方法类
template <>
struct JConstructionMethod<void>
{
	typedef std::function<void*(void)>  ConstructMethod;
	ConstructMethod constructMethod;
	JConstructionMethod(ConstructMethod &&constructMethod) :
		constructMethod(std::forward<ConstructMethod>(constructMethod)) {}
};

template <>
struct JConstructionMethod<>
{
	typedef std::function<void*(void)>  ConstructMethod;
	ConstructMethod constructMethod;
	JConstructionMethod(ConstructMethod &&constructMethod) :
		constructMethod(std::forward<ConstructMethod>(constructMethod)) {}
};

/*-----------------------------------------------------------------------------------*/

// 构造方法表
template <typename ...Args>
class JConstructionMethodHash
{
	static JConstructionMethodHash &instance();
	inline JConstructionMethodHash() {}
	inline ~JConstructionMethodHash() {}

	typedef typename JConstructionMethod<Args...>::ConstructMethod   ConstructMethod;

	void registerMethod(size_t id, ConstructMethod &&method);
	ConstructMethod &findConstructMethod(size_t id);

	typedef std::unordered_map<size_t, JConstructionMethod<Args...>> MethodHash;
	MethodHash m_methodHash;

	static ConstructMethod constructNULL;
	friend class JMeta;
	J_DISABLE_COPY(JConstructionMethodHash)
};

// 特殊构造方法表
class JXQLIB_EXPORT JOtherMethodHash
{
	typedef std::function<void(void*,const void*)>  CopyMethod;
	typedef std::unordered_map<size_t, CopyMethod>   CopyMethodHash;
	static CopyMethodHash copyMethodHash;

	static void registerCopyMethod(size_t id, CopyMethod &&method);
	static CopyMethod &findCopyMethod(size_t id);

	typedef std::function<void*(const void*)>       CopyConstructMethod;
	typedef std::unordered_map<size_t, CopyConstructMethod>  CopyConstructMethodHash;
	static CopyConstructMethodHash copyConstructMethodHash;

	static void registerCopyConstructMethod(size_t id, CopyConstructMethod &&method);
	static CopyConstructMethod &findCopyConstructMethod(size_t id);

	typedef std::function<void(const void*)>            DestructMethod;
	typedef std::unordered_map<size_t, DestructMethod>  DestructMethodHash;
	static DestructMethodHash destructMethodHash;

	static void registerDestructMethod(size_t id, DestructMethod &&method);
	static DestructMethod &findDestructMethod(size_t id);

	friend class JMeta;
};

template <typename ...Args>
inline JConstructionMethodHash<Args...>&
JConstructionMethodHash<Args...>::instance()
{
	static JConstructionMethodHash uniqueObject;
	return uniqueObject;
}

template <typename ...Args>
inline void JConstructionMethodHash<Args...>::
registerMethod(size_t id, ConstructMethod &&method)
{
	m_methodHash.emplace(std::piecewise_construct,
						 std::forward_as_tuple(id),
						 std::forward_as_tuple(
							 JConstructionMethod<Args...>(
								 std::forward<ConstructMethod>(method))));
}

template <typename ...Args>
inline typename JConstructionMethodHash<Args...>::ConstructMethod&
JConstructionMethodHash<Args...>::findConstructMethod(size_t id)
{
	auto it = m_methodHash.find(id);
	if( it == m_methodHash.end() )
	{
		jError() << "Object cannot be constructed, constructor cannot be found: type id =" << id;
		return constructNULL;
	}
	return it->second.constructMethod;
}

template <typename ...Args>
typename JConstructionMethodHash<Args...>::ConstructMethod
JConstructionMethodHash<Args...>::constructNULL = nullptr;

/*-----------------------------------------------------------------------------------*/

// 构造
template <typename Type, typename ...Args>
inline void *jTypeConstructHelper(Args&& ...args)
{
	return new Type(std::forward<Args>(args)...);
}

// 拷贝赋值
template <typename Type>
inline void jTypeCopyHelper(void *s, const void *d)
{
	Type *t_s = J_RCT(Type*, s);
	const Type *t_d = J_RCT(const Type*, d);
	*t_s = *t_d;
}

// 拷贝构造
template <typename Type>
inline void *jTypeCopyConstructHelper(const void *t)
{
	return new Type(*J_RCT(Type*, J_CCT(void*, t)));
}

// 析构
template <typename Type>
inline void jTypeDestructHelper(const void *t)
{
	delete J_RCT(const Type*, t);
}

/*-----------------------------------------------------------------------------------*/

template <typename T> inline constexpr
const char *JTypeInfo::getName(T)
{ return ABI_CXA_DEMANGLE(typeid(T).name()); }

template <typename T> inline constexpr
const char *JTypeInfo::getName()
{ return ABI_CXA_DEMANGLE(typeid(T).name()); }

template <typename T> struct __is_primitive           { enum{ value = false }; };
template <> struct __is_primitive<void>               { enum{ value = false }; };
template <> struct __is_primitive<bool>               { enum{ value = true  }; };
template <> struct __is_primitive<char>               { enum{ value = true  }; };
template <> struct __is_primitive<signed char>        { enum{ value = true  }; };
template <> struct __is_primitive<unsigned char>      { enum{ value = true  }; };
template <> struct __is_primitive<wchar_t>            { enum{ value = true  }; };
template <> struct __is_primitive<short>              { enum{ value = true  }; };
template <> struct __is_primitive<unsigned short>     { enum{ value = true  }; };
template <> struct __is_primitive<int>                { enum{ value = true  }; };
template <> struct __is_primitive<unsigned int>       { enum{ value = true  }; };
template <> struct __is_primitive<long>               { enum{ value = true  }; };
template <> struct __is_primitive<unsigned long>      { enum{ value = true  }; };
template <> struct __is_primitive<long long>          { enum{ value = true  }; };
template <> struct __is_primitive<unsigned long long> { enum{ value = true  }; };
template <> struct __is_primitive<float>              { enum{ value = true  }; };
template <> struct __is_primitive<double>             { enum{ value = true  }; };
template <> struct __is_primitive<long double>        { enum{ value = true  }; };

template <typename T>
inline constexpr bool JTypeInfo::isPrimitive()
{ return __is_primitive<T>::value or std::is_pointer<T>::value; }

template <typename T>
inline constexpr bool JTypeInfo::isPrimitive(T)
{ return __is_primitive<T>::value or std::is_pointer<T>::value; }

template <typename T> struct __is_float    { enum{ value = false }; };
template <> struct __is_float<float>       { enum{ value = true }; };
template <> struct __is_float<double>      { enum{ value = true }; };
template <> struct __is_float<long double> { enum{ value = true }; };

template <typename T>
inline constexpr bool JTypeInfo::isFloat()
{ return __is_float<T>::value; }

template <typename T>
inline constexpr bool JTypeInfo::isFloat(T)
{ return __is_float<T>::value; }

template <typename T>
inline constexpr bool JTypeInfo::isPointer()
{ return std::is_pointer<T>::value; }

template <typename T>
inline constexpr bool JTypeInfo::isPointer(T)
{ return std::is_pointer<T>::value; }

template <typename T>
inline const JXQ::JTypeInfo &JTypeIDConstructHelper::jtypeid()
{
	static JXQ::JTypeInfo info(
				typeid(T), sizeof(T),
				__is_primitive<T>::value,
				__is_float<T>::value,
				std::is_pointer<T>::value
				);
	return info;
}

} //namespace JXQ

// 获得类型信息
template <typename T>
inline const JXQ::JTypeInfo &jtypeid()
{ return JXQ::JTypeIDConstructHelper::jtypeid<T>(); }

template <typename T>
inline const JXQ::JTypeInfo &jtypeid(T)
{ return JXQ::JTypeIDConstructHelper::jtypeid<T>(); }


#endif //JMETATYPE_H
